package com.quwan.app.controller;

import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Maps;
import com.quwan.app.service.PaymentService;
import com.quwan.app.vo.pay.ApplePayData;
import com.quwan.app.vo.pay.ApplePayResult;
import com.quwan.app.vo.pay.NotifyProcessParams;
import com.quwan.common.BaseController;
import com.quwan.common.Check;
import com.quwan.common.enums.BalancePayTypeEnum;
import com.quwan.common.enums.PaymentEnum;
import com.quwan.common.exception.ExpCode;
import com.quwan.common.exception.ResponseException;
import com.quwan.common.result.ApiResult;
import com.quwan.component.AppleComponent;
import com.quwan.component.ComponentFactory;
import com.quwan.constans.CacheKey;
import com.quwan.entity.TSystemPrePay;
import com.quwan.utils.PayUtil;
import com.quwan.utils.redis.Redis;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;
import java.util.function.Predicate;


/**
 * @author quan
 * @date 2021-10-27 16:44
 */
@Api(tags = "支付确认")
@Slf4j
@RestController
@RequestMapping("app/payment")
@RequiredArgsConstructor
public class PaymentConfirmController extends BaseController {
    private final PaymentService paymentService;
    private final AppleComponent appleComponent;
    private final ComponentFactory componentFactory;
    private final Redis redis;
    private final RedissonClient redisson;


    @ApiOperation(value = "Apple支付校验接口",
            notes = "苹果订单 json参数 ")
    @PostMapping("apple/pay")
    public ApiResult applePay(HttpServletRequest request,@RequestBody ApplePayData data) {
        if (redis.hexists(CacheKey.DATA.applePayVerify,data.getTransaction_id()))
            return ApiResult.successMsg("已处理过的支付信息");

        ApplePayResult result = appleComponent.applePayVerify(data.getReceipt_data(), data.getTransaction_id());
        boolean success = result.isSuccess();
        if (!success)
            return ApiResult.error(400, result.getMsg());
        RLock lock = redisson.getLock("lock:" + data.getTransaction_id());

        try {
            lock.lock();
            Boolean setNX = redis.hSetNX(CacheKey.DATA.applePayVerify, data.getTransaction_id(), result.getData().toJSONString());
            if (!setNX){
                log.info("已处理过的支付信息：：{}",result.getData());
                ApiResult<Object> res = ApiResult.successMsg("已处理过的支付信息");
                res.setData(result.getData());
                return res;
            }
            else{
                //会员发放
                log.info("会员发放：：{}",result.getData());
                String id = "";
                try {
                     id = getId(request);
                } catch (Exception e) {
                }
                if (StringUtils.isNotBlank(id))
                    paymentService.doGrantVip(id,result.getData());
                else if (StringUtils.isNotBlank(data.getUnique()))
                    paymentService.recordUniqueInfo(data.getUnique(),result.getData());
                else
                    return ApiResult.error(ExpCode.PARAMS_UNKNOWN);
                  return ApiResult.success(result.getData());
            }
        } finally {
            if (lock.isLocked() && lock.isHeldByCurrentThread())
                lock.unlock();
        }

    }

    /**
     * 系统余额支付 确认支付
     * 请求头要有token
     *
     * @param body    dataType =json  params{orderId,preId,type,payKey} type:0订单购买 1充值会员
     * @param request
     * @return
     */
    @ApiOperation(value = "系统支付确认接口",
            notes = "dataType =json  params{orderId:‘订单id’,preId:预支付id,type:0订单购买 1充值会员 2派单支付,3派单定金支付,payKey:支付密码} ")
    @PostMapping("consume/balance")
    public ApiResult confirmBalancePay(HttpServletRequest request, @RequestBody String body) {
        String memberId = getId(request);
        JSONObject params = JSONObject.parseObject(body);
        String orderId = params.getString("orderId");
        String preId = params.getString("preId");
        String type = params.getString("type");
        String payKey = params.getString("payKey");

        if (StringUtils.isBlank(payKey) || StringUtils.isBlank(orderId) || StringUtils.isBlank(preId) || StringUtils.isBlank(type))
            return ApiResult.error(ExpCode.PARAMS_ILLEGAL.getMsg());

        paymentService
                .doBalancePay(
                        orderId,
                        preId,
                        memberId,
                        type,
                        payKey,
                        instanceByType(type)
                );
        return ApiResult.success();


    }

    private Predicate<TSystemPrePay> instanceByType(String type) {

        BalancePayTypeEnum payType = BalancePayTypeEnum.getByType(type);
        Check.notNull(payType, ExpCode.PAY_NOT_SUPPORT);

        switch (payType) {
            default:
                throw new ResponseException(ExpCode.PAY_NOT_SUPPORT.getCode(), ExpCode.PAY_NOT_SUPPORT.getMsg());
            case CONSUME_ORDER: //下单消费
                return (systemPrePay) -> paymentService.updOrderPayFinishInfo(new NotifyProcessParams(String.valueOf(systemPrePay.getTradNo()), String.valueOf(systemPrePay.getTransactionId()), String.valueOf(systemPrePay.getMoney())));
            case RECHARGE_VIP: //vip充值
                return (systemPrePay) -> paymentService.updVipPayFinishInfo(new NotifyProcessParams(String.valueOf(systemPrePay.getTradNo()), String.valueOf(systemPrePay.getTransactionId()), String.valueOf(systemPrePay.getMoney())));
            case DISPATCH_ORDER: //派单支付
                return (systemPrePay) -> paymentService.updDispatchOrderPayFinishInfo(new NotifyProcessParams(String.valueOf(systemPrePay.getTradNo()), String.valueOf(systemPrePay.getTransactionId()), String.valueOf(systemPrePay.getMoney())));
            case DISPATCH_BEFORE: //派单定金支付
                return (systemPrePay) -> paymentService.updBeforeDispatchOrderPayFinishInfo(new NotifyProcessParams(String.valueOf(systemPrePay.getTradNo()), String.valueOf(systemPrePay.getTransactionId()), String.valueOf(systemPrePay.getMoney())));
        }

    }

    /**
     * 微信支付订单异步通知处理
     *
     * @param body
     * @return
     * @throws Exception
     */
    @PostMapping("notify/wx_order")
    public ResponseEntity<String> wxOrderNotify(@RequestBody String body) throws Exception {
        log.debug("微信支付回调报文::{}", body);
        Map<String, Object> map = PayUtil.getMapFromXML(body);
        final Map<String, String> params = Maps.transformEntries(map, (k, v) -> String.valueOf(v));
        log.info("解析body 回调数据:: \n" + params);
        String buyIt = params.get("attach");
        Function<Map<String, String>, String> notify = componentFactory.getNotifyInstance(PaymentEnum.getByBuyIt(buyIt), buyIt);
        String resXml = notify.apply(params);

        log.info("回调响应:: \n" + resXml);
        return new ResponseEntity<String>(resXml, HttpStatus.OK);
    }

    /**
     * 支付宝 结果通知
     *
     * @param
     * @return
     * @throws Exception
     */
    @PostMapping("notify/zfb")
    public void zfbOrderNotify(HttpServletRequest request, HttpServletResponse response) throws Exception {
        Map<String, String> params = new HashMap<>();
        Enumeration<String> parameterNames = request.getParameterNames();
        while (parameterNames.hasMoreElements()) {
            String key = parameterNames.nextElement();
            params.put(key, request.getParameter(key));
        }
        String buyIt = params.get("passback_params");
        log.info("响应内容：{}", params);
        Function<Map<String, String>, String> notify = componentFactory.getNotifyInstance(PaymentEnum.getByBuyIt(buyIt), buyIt);
        String ret = notify.apply(params);
        response.getWriter().write(ret);

    }

}
